package cn.ac.big.bigd.api.controller;


import cn.ac.big.bigd.constant.ResultStatus;
import cn.ac.big.bigd.dao.BlastDao;
import cn.ac.big.bigd.dto.Result;
import cn.ac.big.bigd.model.Blast;
import cn.ac.big.bigd.model.submit.Submission;
import cn.ac.big.bigd.service.ScriptService;
import cn.ac.big.bigd.util.AccessionUtil;
import cn.ac.big.bigd.util.Constant;
import cn.ac.big.bigd.util.EmailUtils;
import cn.ac.big.bigd.util.FileUtil;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.ui.Model;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLConnection;
import java.nio.file.Paths;
import java.util.Calendar;
import java.util.Date;

/**
 * Created by wangfan on 2017/9/27.
 */

@RestController
@RequestMapping("api/analysis")
public class AnalysisController {

    private static final Logger logger = LoggerFactory.getLogger(AnalysisController.class);
    String blastDb = "/disk2/software/blastdb/";
    @Autowired
    ScriptService scriptService;

    @Autowired
    BlastDao blastDao;

    @RequestMapping(value = "/blast/saveBlast", method = RequestMethod.POST)
    public Result saveBlast(Model model,
                            MultipartFile queryFile,
                            Integer queryRadio,
                            String queryText,
                            String querySequenceType,

                            Integer targerData,

                            MultipartFile targetFile,
                            Integer targetRadio,
                            String targetText,
                            String targetSequenceType,
                            String databaseSource,
                            Integer outFormat,//默认6

                            String programName,
                            String task,

                            String maxTargetSeqs,

                            String evalue,
                            String wordSize,
                            String matchReward,
                            String mismatch,
                            String gapopen,
                            String gapextend,
                            String lcaseMaksing,
                            String softMasking,

                            String jobTitle,

                            String notifyEmail,

                            Boolean newWindow,

                            Blast blast,
                            HttpServletRequest request,
                            HttpServletResponse response) throws Exception {

//        response.setHeader("Access-Control-Allow-Origin", "*");
//        response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");


        Date date = new Date();
        AccessionUtil.addBlast();
        String accession = AccessionUtil.blastAccession;
        blast.setAccession(accession);
        blast.setCreatedAt(date);
        String directory = "/disk2/gwhfileprocess/" + accession + "/";
        File f = new File(directory);
        if (!f.exists()) {
            f.mkdir();
        }
        String scriptPath = directory + accession + ".sh";
        PrintWriter pw = new PrintWriter(new FileWriter(scriptPath));
        pw.println("#!/bin/sh");
        pw.println("#PBS -q batch");
        pw.println("#PBS -N " + accession);
        pw.println("#PBS -e " + directory + "" + accession + ".blast.err");
        pw.println("#PBS -o " + directory + "" + accession + ".blast.out");
        pw.println("#PBS -l mem=16gb,walltime=24:00:00,nodes=1:ppn=1");
        pw.println("#HSCHED -s hschedd");
        pw.println("cd " + directory);


        blast.setQueryRadio(queryRadio);
        String queryFilePath = Paths.get(directory, "queryFile.fasta").toString();
        String targetFilePath = Paths.get(directory, "targetFile.fasta").toString();
        if (queryRadio == 0) {
            if (!queryFile.isEmpty()) {
                if (FileUtil.saveFile(queryFile.getBytes(), queryFilePath))
                    return Result.build(1, "save query  File error", "");
            } else {
                return Result.build(1, "query File is empty", "");
            }
        } else if (queryRadio == 1) {  //textarea
            if (FileUtil.saveFile(queryText.getBytes(), queryFilePath))
                return Result.build(1, "save target  File error", "");

        } else {
            return Result.build(1, "please check the radio", "");
        }

        blast.setQuerySequenceType(querySequenceType);
        File q = new File(queryFilePath);
        if (q.length() > 100 * 1024 * 1024) {
            return Result.build(1, "Query sequence size > 100M", "");
        }
        blast.setQueryFilePath(queryFilePath);

        blast.setTargerData(targerData);
        if (targerData == 0) {
            blast.setTargetRadio(targetRadio);
            if (targetRadio == 0) {
                if (!targetFile.isEmpty()) {
                    if (FileUtil.saveFile(targetFile.getBytes(), targetFilePath))
                        return Result.build(1, "save target  File error", "");
                } else {
                    return Result.build(1, "target File is empty", "");
                }
            } else if (targetRadio == 1) {  //textarea
                if (FileUtil.saveFile(targetText.getBytes(), targetFilePath))
                    return Result.build(1, "save target  File error", "");

            } else {
                return Result.build(1, "please check the radio", "");
            }

            File t = new File(targetFilePath);
            if (t.length() > 100 * 1024 * 1024) {
                return Result.build(1, "Target sequence size > 100M", "");
            }


            blast.setTargetSequenceType(targetSequenceType);
            blast.setTargetFilePath(targetFilePath);
            pw.println("makeblastdb  "
                    + " -dbtype " + targetSequenceType
                    + " -in " + targetFilePath
                    + " -input_type fasta"
                    + " -out " + directory + accession
                    + " -logfile " + directory + "makedb" + accession + ".log"
            );

            blast.setDatabaseSource(directory + accession);
        } else if (targerData == 1) {
            if (databaseSource.isEmpty()) {
                return Result.build(1, "databaseSource empty", "");
            }
            blast.setDatabaseSource(blastDb + databaseSource);
        } else {
            return Result.build(1, "check", "");
        }
        blast.setProgramName(programName);
        String str = programName
                + " -query " + blast.getQueryFilePath()
                + " -db " + blast.getDatabaseSource()
                + " -out " + directory + accession + ".out";
        if (null != outFormat) {
            blast.setOutFormat(outFormat);
            str = str + " -outfmt " + outFormat;
        }
        if (null != task && !task.isEmpty()) {
            blast.setTask(task);
            str = str + " -task " + task;
        }

        if (null != maxTargetSeqs && !maxTargetSeqs.isEmpty()) {
            blast.setMaxTargetSeqs(maxTargetSeqs);
            str = str + " -max_target_seqs " + maxTargetSeqs;
        }

        if (null != wordSize && !wordSize.isEmpty()) {
            blast.setWordSize(wordSize);
            str = str + " -word_size " + wordSize;
        }
        if (null != matchReward && !matchReward.isEmpty()) {
            blast.setMatchReward(matchReward);
            str = str + " -reward " + matchReward;
        }
        if (null != mismatch && !mismatch.isEmpty()) {
            blast.setMismatch(mismatch);
            str = str + " -penalty " + mismatch;
        }

        if (null != gapopen && !gapopen.isEmpty()) {
            blast.setGapopen(gapopen);
            str = str + " -gapopen " + gapopen;
        }
        if (null != gapextend && !gapextend.isEmpty()) {
            blast.setGapextend(gapextend);
            str = str + " -gapextend " + gapextend;
        }

        if (null != lcaseMaksing && !lcaseMaksing.isEmpty()) {
            blast.setLcaseMaksing(lcaseMaksing);
            str = str + " -lcase_maksing ";
        }
        if (null != softMasking && !softMasking.isEmpty()) {
            blast.setSoftMasking(softMasking);
            str = str + " -soft_masking " + softMasking;
        }
        pw.println(str);
        pw.println("echo \"DoneFinished!\"");
        pw.close();
        blast.setStatus(0);//submit

        blast = blastDao.save(blast);

        return Result.build(0, "success", blast);

    }

    @RequestMapping(value = "/blast/run", method = RequestMethod.POST)
    public Result runBlast(Model model, String accession,

                           HttpServletRequest request,
                           HttpServletResponse response) throws Exception {

//        response.setHeader("Access-Control-Allow-Origin", "*");
//        response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");


        Blast blast = blastDao.findByAccession(accession);
        if (null == blast) {
            return new Result(1, "error", "");
        }
        String directory = "/disk2/gwhfileprocess/" + accession + "/";
        File f = new File(directory);
        if (!f.exists()) {
            f.mkdir();
        }
        String scriptPath = directory + accession + ".sh";
        logger.info("start execute dsub blast  script....");
        String[] args = new String[]{"dsub", scriptPath};
        String  jobId=scriptService.getProcessJobId(args);
        if (null!=jobId) {
            logger.info("end  dsub  blast  script....");
        } else {
            return Result.build(1, "dsub blast script  error", "");
        }
        blast.setStatus(1);//running
        blast = blastDao.save(blast);

        //判断sh脚本是否运行结束
        if (scriptService.isDsubJobFinished(jobId, directory)) {
            logger.info("finish job:" + accession);
        } else {
            logger.info("qstat error");
            return Result.build(1, "qstat error", "");
        }


        File outFile = new File(directory + "" + accession + ".blast.out");
        File errFile = new File(directory + "" + accession + ".blast.err");

        if (outFile.isFile() && outFile.exists()) { //判断out文件是否存在
            InputStreamReader read = new InputStreamReader(
                    new FileInputStream(outFile));
            BufferedReader bufferedReader = new BufferedReader(read);
            String outStr = "";
            String lineTxt = null;
            while ((lineTxt = bufferedReader.readLine()) != null) {
                outStr = outStr + lineTxt + "\n";
            }
            read.close();
            logger.info(outStr);
            if (outStr.contains("DoneFinished!")) {
                logger.info("outFile normal....");
            } else {
                logger.info("outFile abnormal....");
                //非正常结束发送邮件给管理员
                sendAbnormalEmail("wangfan@big.ac.cn", directory);
                return new Result(ResultStatus.ERROR, "has error", blast);
            }

        }

        if (errFile.isFile() && errFile.exists() && errFile.length() == 0) {
            logger.info("errFile normal....");

        } else {
            logger.info("errFile abnormal....");
            //非正常结束发送邮件给管理员
            sendAbnormalEmail("wangfan@big.ac.cn", directory);
            return new Result(ResultStatus.ERROR, "errFile has error", blast);
        }

        Calendar c = Calendar.getInstance();
        // 现在的时间(单位：毫秒)
        long curtime = c.getTimeInMillis();
        blast.setFinishTime(String.valueOf(curtime));
        long exptime = (curtime + Constant.expTime);
        blast.setExpireTime(String.valueOf(exptime));
        blast.setOutFilePath(directory + "" + accession + ".out");
        blast.setStatus(2);//finish
        blast = blastDao.save(blast);

        if(null!=blast.getNotifyEmail()&&!blast.getNotifyEmail().isEmpty()){
            sendNotifyEmail(blast);
        }
        File joboutFile = new File(directory + "" + accession + ".out");
        if (joboutFile.isFile() && joboutFile.exists() && joboutFile.length() == 0) {
            logger.info("joboutFile normal....");

            return new Result(2, "Not results", blast);
        } else {
            logger.info("joboutFile abnormal....");
            return Result.build(0, "success", blast);
        }

    }


    private static void sendAbnormalEmail(String email, String savePath) {
        StringBuffer sb = new StringBuffer("<div style=\"padding:24px 20px;font: bold 14px/22px arial,sans-serif;\">Dear Admin ,"
                + "<br/>"
                + "<br/><br/>Blast Path:" + savePath
                + "<br/><br/>User:" + email
                + "<br/><br/>Sincerely,<br/>GWH team<br/><br/></div>");

        // 发送邮件
        EmailUtils.sendHtmlMail("Blast Mission error", sb.toString(), "wangfan@big.ac.cn,chenml@big.ac.cn");

    }

    private static void sendNotifyEmail(Blast blast) {
        StringBuffer sb = new StringBuffer("<div style=\"padding:24px 20px;font: bold 14px/22px arial,sans-serif;\">Dear user,"
                + "<br/>"
                + "Your alignment jod ("+blast.getAccession()+") is finished using BLAST 2.6.0+."
                + "<br/>The URL of your result page is following address:"
                + "<br/><a href='http://bigd.big.ac.cn/gwh/analysis/blast/getResult?accession="+blast.getAccession()+"'>http://bigd.big.ac.cn/gwh/analysis/blast/getResult?accession="+blast.getAccession()+"</a>"
                + "<br/><br/>Sincerely,<br/>GWH team<br/><br/></div>");

        // 发送邮件
        EmailUtils.sendHtmlMail("Blast("+blast.getAccession()+") job is finished", sb.toString(), blast.getNotifyEmail());

    }


    @RequestMapping(value = "/downloadfile", method = RequestMethod.GET)
    public void downloadFile(HttpServletResponse response, String filepath) throws IOException {
        File file = new File(filepath);
        if (!file.exists()) {
            String errorMessage = "Sorry. The file you are looking for does not exist";
            throw new IOException(errorMessage);

        }

        String mimeType = URLConnection.guessContentTypeFromName(file.getName());
        if (mimeType == null) {
            System.out.println("mimetype is not detectable, will take default");
            mimeType = "application/octet-stream";
        }

        response.setContentType(mimeType);
        response.setHeader("Content-Disposition", String.format("attachment; filename=\"" + file.getName() + "\""));
        response.setHeader("Content-Length", String.valueOf(file.length()));
        response.setContentLength((int) file.length());
        InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
        FileCopyUtils.copy(inputStream, response.getOutputStream());
    }


    @RequestMapping(value = "/b", method = RequestMethod.GET)
    public @ResponseBody
    HttpEntity<byte[]> downloadB(String filepath) throws IOException {
        File file = getFile(filepath);
        byte[] document = FileCopyUtils.copyToByteArray(file);
        HttpHeaders header = new HttpHeaders();
        String mimeType = URLConnection.guessContentTypeFromName(file.getName());
        if (mimeType == null) {
            System.out.println("mimetype is not detectable, will take default");
            mimeType = "application/octet-stream";
        }
        header.setContentType( MediaType.valueOf(mimeType));
        header.set("Content-Disposition", "inline; filename=" + file.getName());
        header.setContentLength(document.length);
        return new HttpEntity<>(document, header);
    }

    @RequestMapping(value = "/c", method = RequestMethod.GET)
    public @ResponseBody
    Resource downloadC(HttpServletResponse response, String filepath) throws FileNotFoundException {
        File file = getFile(filepath);
        String mimeType = URLConnection.guessContentTypeFromName(file.getName());
        if (mimeType == null) {
            System.out.println("mimetype is not detectable, will take default");
            mimeType = "application/octet-stream";
        }

        response.setContentType(mimeType);
        response.setHeader("Content-Disposition", "inline; filename=" + file.getName());
        response.setHeader("Content-Length", String.valueOf(file.length()));
        return new FileSystemResource(file);
    }

    private File getFile(String filepath) throws FileNotFoundException {
        File file = new File(filepath);
        if (!file.exists()) {
            throw new FileNotFoundException("file with path: " + filepath + " was not found.");
        }
        return file;
    }
}
